#include "OperationManager.h"
#include "OP_Delete.h"
#include "OP_Duplicate.h"
#include "OP_SelectAll.h"
#include "OP_DeselectAll.h"
#include "OP_InvertSelection.h"
#include "OP_Hide.h"
#include "OP_Unhide.h"
#include "OP_CreateVertex.h"
#include "OP_CreateTriangle.h"
#include "OP_CreateRectangle.h"
#include "OP_CreateCuboid.h"
#include "OP_CreateSphere.h"
#include "OP_CreateCylinder.h"
#include "OP_CreateCone.h"
#include "OP_CreateTorus.h"
#include "OP_CreateTriangleStrip.h"
#include "OP_SelectGeometries.h"
#include "OP_SelectFaces.h"
#include "OP_SelectVertices.h"
#include "OP_Translate.h"
#include "OP_Scale.h"
#include "OP_Rotate.h"
#include "OP_Extrude.h"

using namespace std;
using namespace CICore;
using namespace CIGeom;
using namespace CIView;

//Static variables, used to set the operations parameters from the user interface
Vector3D CIOps::OperationManager::X_Y_Z_Params = Vector3D();
bool CIOps::OperationManager::CenterOfMass = true;
bool CIOps::OperationManager::Origin = false;
bool CIOps::OperationManager::UserPoint = false;
int CIOps::OperationManager::Stacks = 10;
int CIOps::OperationManager::Slices = 10;
float CIOps::OperationManager::Thickness = 2.0f;
bool CIOps::OperationManager::IgnoreBackfaces = false;

/// <summary>
/// Constructor
/// </summary>
/// <param name="v"> A reference to the vector that contains the four 3D views pointers </param>
CIOps::OperationManager::OperationManager (vector<CIView::View*>& v) : views (v), todoOp (0) {}

/// <summary>
/// Destructor
/// </summary>
CIOps::OperationManager::~OperationManager() {
    //Deleting the operation not completed    
    delete todoOp; todoOp = 0;       

    //Deleting all operations into undoOps stack
    while (undoOps.size()) {
        delete undoOps.top();
        undoOps.pop();
    }

    //Deleting all operations into redoOps stack
    while (redoOps.size()) {
        delete redoOps.top();
        redoOps.pop();
    }
}


///<************************************************************************************************************>
///<**************************************** region Utility Methods ********************************************>
///<************************************************************************************************************>

/// <summary>
/// Method called to create the operation identified by op integer
/// </summary>
/// <param name="opID"> An integer that identifies the operation to create </param>
CIOps::Operation* CIOps::OperationManager::CreateOperation (int opID) {
    DeleteUselessOps();              //Deletes all operations became useless

    if (opID != CREATE_TRIANGLE
        && opID != CREATE_TRIANGLE_STRIP
        && todoOp) {                            //If there is an old operation that hasn't been completed, and it is not a CreateTriangle Operation
            delete todoOp;                      //must delete it
            todoOp = 0;  
    }

    switch (opID) {        
        case CREATE_VERTEX:         //Create-Vertex-Operation   
            todoOp = new OP_CreateVertex (views);
            break;

        case CREATE_TRIANGLE:       //Create-Triangle-Operation
            if (!(todoOp && todoOp->status == Operation::STEP_COMPLETED))
                todoOp = new OP_CreateTriangle (views);
            break;

        case CREATE_TRIANGLE_STRIP: //Create-Triangle-Strip-Operation
            if (!(todoOp && todoOp->status == Operation::STEP_COMPLETED))
                todoOp = new OP_CreateTriangleStrip (views);
            break;

        case CREATE_RECTANGLE:      //Create-Rectangle-Operation   
            todoOp = new OP_CreateRectangle (views);
            break;

        case CREATE_CUBOID:         //Create-Cuboid-Operation   
            todoOp = new OP_CreateCuboid (views);
            break;

        case CREATE_SPHERE:         //Create-Sphere-Operation
            todoOp = new OP_CreateSphere (views);
            break;

        case CREATE_CYLINDER:       //Create-Cylinder-Operation
            todoOp = new OP_CreateCylinder (views);
            break;

        case CREATE_CONE:           //Create-Cone-Operation
            todoOp = new OP_CreateCone (views);
            break;

        case CREATE_TORUS:          //Create-Torus-Operation
            todoOp = new OP_CreateTorus (views);
            break;

        case SELECT_GEOMETRIES:     //Select-Geometries-Operation
            todoOp = new OP_SelectGeometries (views);
            break;

        case SELECT_FACES:          //Select-Faces-Operation
            todoOp = new OP_SelectFaces (views);
            break;

        case SELECT_VERTICES:       //Select-Vertices-Operation
            todoOp = new OP_SelectVertices (views);
            break;

        case TRANSLATE:             //Translate-Primitives-Operation
            todoOp = new OP_Translate (views);
            break;

        case SCALE:                 //Scale-Primitives-Operation
            todoOp = new OP_Scale (views);
            break;

        case ROTATE:                //Rotate-Primitives-Operation
            todoOp = new OP_Rotate (views);
            break;

        case EXTRUDE:               //Extrude-Primitives-Operation
            todoOp = new OP_Extrude (views);
            break;

        default:
            todoOp = 0;
            break;
    }
    return todoOp;
}

/// <summary>
/// Method called to create the action associated with the operation identified by opID
/// </summary>
/// <param name="iconPath"> The path of the icon to associate with the action </param>
/// <param name="tooltip"> The tooltip to associate with the action </param>
/// <param name="shortcut"> The keyboard shortcut to associate with the action </param>
/// <param name="parent"> The parent object of the action </param>
/// <param name="viewsStatus"> The views status when clicked </param>
/// <param name="opID"> The id of the operation to associate with the action </param>
QAction* CIOps::OperationManager::CreateAction (const QString& iconPath, const QString& tooltip, const QString& shortcut, QObject* parent, int viewsStatus, int opID) {
    QAction* action = new ActionOpCreation (QIcon (iconPath), tooltip, parent, viewsStatus, opID);
    action->setShortcut (shortcut);
    action->setStatusTip (tooltip);  
    return action;
}

/// <summary>
/// Method called to check the current operation status
/// </summary>
/// <returns> A boolean to indicate if the operation has been completed </returns>
bool CIOps::OperationManager::CheckOperationStatus() {
    if (todoOp == 0)
        return false;

    switch (todoOp->status) {           //Checks the operation status
        case Operation::COMPLETED:
            undoOps.push (todoOp);      //Inserts the last operation executed into the stack of operations that can be canceled
            todoOp = 0;                 //Changes the pointer of the current operation, due to the operation has been already completed
            return true;
            break;     

        case Operation::ZOMBIE:             
            delete todoOp;
            todoOp = 0;                 //Changes the pointer of the current operation, due to the operation has been already completed            
            break; 
    }
    return false;
}

/// <summary>
/// //Method called to create and execute the operation identified by opID integer
/// </summary>
void CIOps::OperationManager::DoOperation (int opID) {
    Operation* op;

    switch (opID) {        
        case CREATE_VERTEX:         //Create-Vertex-Operation   
            op = new OP_CreateVertex (views);
            break;

        case CREATE_TRIANGLE:       //Create-Triangle-Operation
            op = new OP_CreateTriangle (views);
            break;

        case CREATE_TRIANGLE_STRIP: //Create-Triangle-Strip-Operation
            op = new OP_CreateTriangleStrip (views);
            break;

        case CREATE_RECTANGLE:      //Create-Rectangle-Operation   
            op = new OP_CreateRectangle (views);
            break;

        case CREATE_CUBOID:         //Create-Cuboid-Operation   
            op = new OP_CreateCuboid (views);
            break;

        case CREATE_SPHERE:         //Create-Sphere-Operation
            op = new OP_CreateSphere (views);
            break;

        case CREATE_CYLINDER:       //Create-Cylinder-Operation
            op = new OP_CreateCylinder (views);
            break;

        case CREATE_CONE:           //Create-Cone-Operation
            op = new OP_CreateCone (views);
            break;

        case CREATE_TORUS:          //Create-Torus-Operation
            op = new OP_CreateTorus (views);
            break;

        case SELECT_GEOMETRIES:     //Select-Geometries-Operation
            op = new OP_SelectGeometries (views);
            break;

        case SELECT_FACES:          //Select-Faces-Operation
            op = new OP_SelectFaces (views);
            break;

        case SELECT_VERTICES:       //Select-Vertices-Operation
            op = new OP_SelectVertices (views);
            break;

        case TRANSLATE:             //Translate-Primitives-Operation
            op = new OP_Translate (views);
            break;

        case SCALE:                 //Scale-Primitives-Operation
            op = new OP_Scale (views);
            break;

        case ROTATE:                //Rotate-Primitives-Operation
            op = new OP_Rotate (views);
            break;

        case EXTRUDE:               //Extrude-Primitives-Operation
            op = new OP_Extrude (views);
            break;

        case DELETE_SELECTION:      //Delete-Primitives-Operation
            op = new OP_Delete (views);
            break;

        case DUPLICATE_SELECTION:   //Duplicate-Primitives-Operation
            op = new OP_Duplicate (views);
            break;

        case SELECT_ALL:            //Select-All-Operation
            op = new OP_SelectAll (views);
            break;

        case DESELECT_ALL:          //Deselect-All-Operation
            op = new OP_DeselectAll (views);
            break;

        case INVERT_SELECTION:      //Invert-Selection-Operation
            op = new OP_InvertSelection (views);
            break;

        case HIDE:                  //Hide-Selection-Operation
            op = new OP_Hide (views);
            break;

        case UNHIDE:               //Unhide-All-Operation
            op = new OP_Unhide (views);
            break;

        default:
            op = 0;
            break;
    }
    if (op) {
        DeleteUselessOps();              //Deletes all operations became useless
        delete todoOp;
        todoOp = 0;  

        if (op->Exec() == Operation::COMPLETED)     //If the operation has been completed correctly
            undoOps.push (op);                      //inserts the operation in the top of the undoOps stack, to allow the "undo-operation" action
        else
            delete op;
    }
}

/// <summary>
/// Method called to undo the last user action
/// </summary>
void CIOps::OperationManager::UndoOperation() {
    if (undoOps.size() == 0)        //If there is no operation to undo
        return;                     //must return

    Operation* op = undoOps.top();  //Retrieves, from the top of the undoOps stack, the operation to undo 
    undoOps.pop();                  //Removes the operation from the top of the undoOps stack
    op->Undo();                     //Undoes the operation

    redoOps.push (op);              //Inserts the operation in the top of the redoOps stack, to allow the "redo-operation" action
}

/// <summary>
/// Method called to redo the last user action
/// </summary>
void CIOps::OperationManager::RedoOperation() {
    if (redoOps.size() == 0)        //If there is no operation to redo
        return;                     //must return

    Operation* op = redoOps.top();  //Retrieves, from the top of the redoOps stack, the operation to redo 
    redoOps.pop();                  //Removes the operation from the top of the redoOps stack
    op->Redo();                     //Redoes the operation

    undoOps.push (op);              //Inserts the operation in the top of the undoOps stack, to allow the "undo-operation" action
}

/// <summary>
/// Method called whenever a new operation has been created, to delete all operations became useless
/// </summary>
void CIOps::OperationManager::DeleteUselessOps() {
    //Deleting all operations into redoOps stack
    while (redoOps.size()) {
        delete redoOps.top();
        redoOps.pop();
    }    
}

/// <summary>
/// Method called whenever a new operation has been created, to delete all operations became useless
/// </summary>
void CIOps::OperationManager::Reset() {
    //Deleting the operation not completed    
    delete todoOp; todoOp = 0;       

    //Deleting all operations into undoOps stack
    while (undoOps.size()) {
        delete undoOps.top();
        undoOps.pop();
    }

    //Deleting all operations into redoOps stack
    while (redoOps.size()) {
        delete redoOps.top();
        redoOps.pop();
    } 
}
/***************************************************************************************************************
***************************************** endregion Utility Methods ********************************************
****************************************************************************************************************/
